%{
#pragma warning(disable: 4996)
int yylex(void);

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "Common.h"
#include "Symbol.h"
#include "ParseNode.h"
#include "GenerateICode.h"

/*
#ifndef _DEBUG
	#define printf(...)
#endif
*/

extern int yyleng;
extern char *yytext;
unsigned int row = 0, col = 0;
char identifierBuff[IDENTIFIER_NAME_MAX_LEN];
unsigned int identifierLen;


void yyerror(const char* msg);


struct SemanticValueStruct
{
	union
	{
		struct ParseNode* pn;

		struct Symbol* symbol;

		// For constants
		Float f;
		Float32 f32;
		Float64 f64;

		Int s;
		Int16 s16;
		Int32 s32;

		UInt u;
		UInt16 u16;
		UInt32 u32;

	};

	Int8 name[IDENTIFIER_NAME_MAX_LEN];
};

#define YYSTYPE struct SemanticValueStruct

#ifdef _MSC_VER
#define BISON_PRINT(STRING, ...) printf("BISON: " STRING "\n", __VA_ARGS__)
#define FLEX_PRINT(STRING, ...) printf("FLEX : " STRING "\n", __VA_ARGS__)
#else
#define BISON_PRINT(STRING, ...) printf("BISON: " STRING "\n", ##__VA_ARGS__)
#define FLEX_PRINT(STRING, ...) printf("FLEX : " STRING "\n", ##__VA_ARGS__)
#endif

%}

%token MUL_ASSIGN DIV_ASSIGN ADD_ASSIGN SUB_ASSIGN INCREMENT DECREMENT GOTO

%token INT_CONST UINT_CONST FLOAT_CONST

%token CONSTANT IDENTIFIER

%start translation_unit
%%

translation_unit
	: /* empty */
	{
		BISON_PRINT("translation_unit -> /* empty */");
	}

	| statement translation_unit
	{
		*statements-- = $1.pn;

		BISON_PRINT("translation_unit -> translation_unit statement");
	}
	;

statement
	: labeled_statement
	{
		$$.pn = $1.pn;

		BISON_PRINT("statement -> labeled_statement");
	}

	| jump_statement
	{
		$$.pn = $1.pn;

		BISON_PRINT("statement -> jump_statement");
	}

	| expression_statement
	{
		$$.pn = $1.pn;

		BISON_PRINT("statement -> expression_statement");
	}
	;

labeled_statement
	: IDENTIFIER ':'
	{
		$$.pn = NULL;

		BISON_PRINT("labeled_statement -> IDENTIFIER ':'");
	}
	;

jump_statement
	: GOTO IDENTIFIER ';'
	{
		PN_ALLOC($$.pn, struct GotoParseNode);
		PN_SET_TYPE($$.pn, PNT_GOTO);
		PSUB_PARSENODE($$.pn, struct GotoParseNode)->arg0 = $2.pn;

		BISON_PRINT("jump_statement -> GOTO IDENTIFIER ';'");
	}

	| GOTO CONSTANT ';'
	{
		PN_ALLOC($$.pn, struct GotoParseNode);
		PN_SET_TYPE($$.pn, PNT_GOTO);
		PSUB_PARSENODE($$.pn, struct GotoParseNode)->arg0 = $2.pn;

		BISON_PRINT("jump_statement -> GOTO CONSTANT ';'");
	}
	;

expression_statement
	: expression ';'
	{
		$$.pn = $1.pn;

		BISON_PRINT("expression_statement -> expression ';'");
	}

	| ';'
	{
		$$.pn = NULL;

		BISON_PRINT("expression_statement -> ';'");
	}
	;

expression
	: assignment_expression
	{
		$$.pn = $1.pn;

		BISON_PRINT("expression -> assignment_expression");
	}
	;

assignment_expression
	: additive_expression
	{
		$$.pn = $1.pn;

		BISON_PRINT("assignment_expression -> additive_expression");
	}

	| unary_expression '=' assignment_expression
	{
		if (!PSUB_PARSENODE($1.pn, struct IdentifierParseNode)->identifier)
			PSUB_PARSENODE($1.pn, struct IdentifierParseNode)->identifier = AddVariableSymbol($1.name);

		PN_ALLOC($$.pn, struct AssignParseNode);
		PN_SET_TYPE($$.pn, PNT_ASSIGN);
		PSUB_PARSENODE($$.pn, struct AssignParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct AssignParseNode)->arg1 = $3.pn;

		BISON_PRINT("assignment_expression -> unary_expression '=' assignment_expression");
	}

	| unary_expression MUL_ASSIGN assignment_expression
	{
		// Assume that a variable symbol already exists
		if (!PSUB_PARSENODE($1.pn, struct IdentifierParseNode)->identifier)
			yyerror("Cannot mul assign to uninitialized variable");

		PN_ALLOC($$.pn, struct MulAssignParseNode);
		PN_SET_TYPE($$.pn, PNT_MUL_ASSIGN);
		PSUB_PARSENODE($$.pn, struct MulAssignParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct MulAssignParseNode)->arg1 = $3.pn;

		BISON_PRINT("assignment_expression -> unary_expression MUL_ASSIGN assignment_expression");
	}

	| unary_expression DIV_ASSIGN assignment_expression
	{
		// Assume that a variable symbol already exists
		if (!PSUB_PARSENODE($1.pn, struct IdentifierParseNode)->identifier)
			yyerror("Cannot div assign to uninitialized variable");

		PN_ALLOC($$.pn, struct DivAssignParseNode);
		PN_SET_TYPE($$.pn, PNT_DIV_ASSIGN);
		PSUB_PARSENODE($$.pn, struct DivAssignParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct DivAssignParseNode)->arg1 = $3.pn;

		BISON_PRINT("assignment_expression -> unary_expression DIV_ASSIGN assignment_expression");
	}

	| unary_expression ADD_ASSIGN assignment_expression
	{
		// Assume that a variable symbol already exists
		if (!PSUB_PARSENODE($1.pn, struct IdentifierParseNode)->identifier)
			yyerror("Cannot add assign to uninitialized variable");

		PN_ALLOC($$.pn, struct AddAssignParseNode);
		PN_SET_TYPE($$.pn, PNT_ADD_ASSIGN);
		PSUB_PARSENODE($$.pn, struct AddAssignParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct AddAssignParseNode)->arg1 = $3.pn;

		BISON_PRINT("assignment_expression -> unary_expression ADD_ASSIGN assignment_expression");
	}

	| unary_expression SUB_ASSIGN assignment_expression
	{
		// Assume that a variable symbol already exists
		if (!PSUB_PARSENODE($1.pn, struct IdentifierParseNode)->identifier)
			yyerror("Cannot sub assign to uninitialized variable");

		PN_ALLOC($$.pn, struct SubAssignParseNode);
		PN_SET_TYPE($$.pn, PNT_SUB_ASSIGN);
		PSUB_PARSENODE($$.pn, struct SubAssignParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct SubAssignParseNode)->arg1 = $3.pn;

		BISON_PRINT("assignment_expression -> unary_expression SUB_ASSIGN assignment_expression");
	}
	;

additive_expression
	: multiplicative_expression
	{
		$$.pn = $1.pn;

		BISON_PRINT("additive_expression -> multiplicative_expression");
	}

	| additive_expression '+' multiplicative_expression
	{
		PN_ALLOC($$.pn, struct AddParseNode);
		PN_SET_TYPE($$.pn, PNT_ADD);
		PSUB_PARSENODE($$.pn, struct AddParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct AddParseNode)->arg1 = $3.pn;

		BISON_PRINT("additive_expression -> additive_expression '+' multiplicative_expression");
	}

	| additive_expression '-' multiplicative_expression
	{
		PN_ALLOC($$.pn, struct SubParseNode);
		PN_SET_TYPE($$.pn, PNT_SUB);
		PSUB_PARSENODE($$.pn, struct SubParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct SubParseNode)->arg1 = $3.pn;

		BISON_PRINT("additive_expression -> additive_expression '-' multiplicative_expression");
	}
	;

multiplicative_expression
	: unary_expression
	{
		$$.pn = $1.pn;

		BISON_PRINT("multiplicative_expression -> unary_expression");
	}

	| multiplicative_expression '*' unary_expression
	{
		PN_ALLOC($$.pn, struct MulParseNode);
		PN_SET_TYPE($$.pn, PNT_MUL);
		PSUB_PARSENODE($$.pn, struct MulParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct MulParseNode)->arg1 = $3.pn;

		BISON_PRINT("multiplicative_expression -> multiplicative_expression '*' unary_expression");
	}

	| multiplicative_expression '/' unary_expression
	{
		PN_ALLOC($$.pn, struct DivParseNode);
		PN_SET_TYPE($$.pn, PNT_DIV);
		PSUB_PARSENODE($$.pn, struct MulParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct MulParseNode)->arg1 = $3.pn;

		BISON_PRINT("multiplicative_expression -> multiplicative_expression '/' unary_expression");
	}
	;

unary_expression
	: postfix_expression
	{
		$$.pn = $1.pn;
		BISON_PRINT("unary_expression -> postfix_expression");
	}

	| INCREMENT unary_expression
	{
		PN_ALLOC($$.pn, struct PreIncrementParseNode);
		PN_SET_TYPE($$.pn, PNT_PRE_INCREMENT);
		PSUB_PARSENODE($$.pn, struct PreIncrementParseNode)->arg0 = $1.pn;

		BISON_PRINT("unary_expression -> INCREMENT unary_expression");
	}

	| DECREMENT unary_expression
	{
		PN_ALLOC($$.pn, struct PreDecrementParseNode);
		PN_SET_TYPE($$.pn, PNT_PRE_DECREMENT);
		PSUB_PARSENODE($$.pn, struct PreDecrementParseNode)->arg0 = $1.pn;

		BISON_PRINT("unary_expression -> DECREMENT unary_expression");
	}
	;

postfix_expression
	: primary_expression
	{
		$$.pn = $1.pn;

		BISON_PRINT("postfix_expression -> primary_expression");
	}

	| postfix_expression INCREMENT
	{
		PN_ALLOC($$.pn, struct PostIncrementParseNode);
		PN_SET_TYPE($$.pn, PNT_POST_INCREMENT);
		PSUB_PARSENODE($$.pn, struct PostIncrementParseNode)->arg0 = $1.pn;

		BISON_PRINT("postfix_expression -> postfix_expression '++'");
	}

	| postfix_expression DECREMENT
	{
		PN_ALLOC($$.pn, struct PostDecrementParseNode);
		PN_SET_TYPE($$.pn, PNT_POST_DECREMENT);
		PSUB_PARSENODE($$.pn, struct PostDecrementParseNode)->arg0 = $1.pn;

		BISON_PRINT("postfix_expression -> postfix_expression '--'");
	}
	;

primary_expression
	: IDENTIFIER
	{
		PN_ALLOC($$.pn, struct IdentifierParseNode);
		PN_SET_TYPE($$.pn, PNT_IDENTIFIER);
		PSUB_PARSENODE($$.pn, struct IdentifierParseNode)->identifier = FindIdentifierSymbol(identifierBuff);

		memcpy($$.name, identifierBuff, identifierLen + 1);

		BISON_PRINT("primary_expression -> IDENTIFIER (%s)", identifierBuff);
	}

	| constant
	{
		$$.pn = $1.pn;

		BISON_PRINT("primary_expression -> CONSTANT");
	}

	| '(' expression ')'
	{
		$$.pn = $2.pn;

		BISON_PRINT("primary_expression -> '(' expression ')'");
	}
	;

constant
	: INT_CONST
	{
		PN_ALLOC($$.pn, struct ConstParseNode);
		PN_SET_TYPE($$.pn, PNT_CONST);
		PSUB_PARSENODE($$.pn, struct ConstParseNode)->value.i32 = $1.s;
		PSUB_PARSENODE($$.pn, struct ConstParseNode)->value.type = CT_INT;

		BISON_PRINT("constant -> INT_CONST (%d)", $1.s);
	}

	| UINT_CONST
	{
		PN_ALLOC($$.pn, struct ConstParseNode);
		PN_SET_TYPE($$.pn, PNT_CONST);
		PSUB_PARSENODE($$.pn, struct ConstParseNode)->value.u32 = $1.u;
		PSUB_PARSENODE($$.pn, struct ConstParseNode)->value.type = CT_UINT;

		BISON_PRINT("constant -> UINT_CONST (%u)", $1.u);
	}

	| FLOAT_CONST
	{
		PN_ALLOC($$.pn, struct ConstParseNode);
		PN_SET_TYPE($$.pn, PNT_CONST);
		PSUB_PARSENODE($$.pn, struct ConstParseNode)->value.f32 = $1.f;
		PSUB_PARSENODE($$.pn, struct ConstParseNode)->value.type = CT_FLOAT;

		BISON_PRINT("constant -> FLOAT_CONST (%f)", $1.f);
	}
	;

%%
#include "lex.yy.c"
